package com.ejy.base.data.config;

import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.ejy.base.data.annotation.CheckRepeatSubmit;
import com.ejy.base.data.constant.NumberConstants;
import com.ejy.base.data.utils.FileUtils;
import com.ejy.commons.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.*;
import java.util.concurrent.TimeUnit;


/**
 * 接口幂等性限制（防止接口重复提交）
 *
 * @Author WangYan
 * @Date 2021/10/13 16:17
 */
@Slf4j
@Aspect
@Component
public class CheckRepeatSubmitConfig {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * api重复提交判断，没有考虑高并发情况
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.ejy.base.data.annotation.CheckRepeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        //获取自定义注解
        CheckRepeatSubmit checkRepeatSubmit = methodSignature.getMethod().getAnnotation(CheckRepeatSubmit.class);
        //redisKey=md5(url+请求参数+文件MD5)
        String url = request.getRequestURL().toString();
        String params = JSONUtil.toJsonStr(request.getParameterMap());
        StringBuffer filesMd5 = new StringBuffer();
        for (Part part : request.getParts()) {
            String[] filename = part.getSubmittedFileName().split("\\.");
            InputStream inputStream = part.getInputStream();
            File tempFile = FileUtils.inputStreamToFile(filename[0], filename[1], inputStream);
            MultipartFile multipartFile = FileUtils.fileToMultipartFile(tempFile);
            String fileMd5 = tempFile == null ? StringUtils.EMPTY : DigestUtils.md5Hex(multipartFile.getBytes());
            filesMd5.append(fileMd5);
        }

        String redisKey = "CheckRepeatSubmit:" + MD5Utils.md5Hex((url + params + filesMd5.toString()).getBytes());
        log.info("========api重复提交判断===========url={}", url);
        if (redisTemplate.hasKey(redisKey)) {
            throw new RuntimeException("请勿重复提交，请稍后再试!");
        }
        redisTemplate.opsForValue().set(redisKey, String.valueOf(NumberConstants.ONE), checkRepeatSubmit.timeout(), TimeUnit.SECONDS);
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            redisTemplate.delete(redisKey);
            throwable.printStackTrace();
            throw new RuntimeException("接口:" + url + " 参数信息：" + params + " 调用异常，错误信息:" + throwable.getMessage());
        }
        //防接口重复提交
        /*finally {
            redisTemplate.delete(token + request.getRequestURI());
            log.info("========api重复提交判断结束，删除key===========");
        }*/
        return proceed;
    }

}
